
/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include <trace_log.h>
#include "umc_avs_enc_compressor.h"
#include "umc_avs_dec_tables.h"
#include "umc_avs_pic.h"

static
Ipp32u AVSQuantTable[64] =
{
    16384,  14888,  13777,  12634,  11585,  10624,  9684,   8885,
    8151,   7512,   6889,   6317,   5813,   5312,   4871,   4479,
    4096,   3756,   3444,   3152,   2896,   2652,   2439,   2233,
    2045,   1878,   1722,   1581,   1447,   1327,   1218,   1117,
    1024,   939,    861,    790,    724,    664,    609,    559,
    512,    469,    431,    395,    362,    332,    304,    279,
    256,    235,    215,    197,    181,    166,    152,    140,
    128,    117,    108,    99,     91,     83,     76,     70
};

namespace UMC
{

void AVSCompressor::DisassembleBMacroBlock(void)
{
    Ipp32u lumaCBP = 0, chromaCBP = 0;
    Ipp32u maxBitSize, mbBitSize = 0, extraBitSize = m_extraBitSize;
    Ipp32u QP, prevQP, chromaQP;
    Ipp16s *psCoeffs, *psTemp;
    Ipp32u foundBestQP;
    Ipp32s i, countCoeff, mb_qp_delta, chromaBlockCount = 0;;
    Ipp32u blockHasCoeffs;
    Ipp16s tempBlock[8 * 8 * 8 + 8];
    Ipp8u *pMCBlock;
    Ipp8u tempMCBlock[16 * 16 + 16];

    psTemp = align_pointer<Ipp16s *> (tempBlock, 16);
    pMCBlock = align_pointer<Ipp8u *> (tempMCBlock, 16);
    Ipp32s pitch = m_disCtx.m_iPitch;

    m_pMbInfo->NumCoeffs[0] = 63;
    m_pMbInfo->NumCoeffs[1] = 63;
    m_pMbInfo->NumCoeffs[2] = 63;
    m_pMbInfo->NumCoeffs[3] = 63;
    m_pMbInfo->NumCoeffs[4] = 63;
    m_pMbInfo->NumCoeffs[5] = 63;
    m_pMbInfo->NumCoeffs[6] = 63;
    m_pMbInfo->NumCoeffs[7] = 63;

    // set the initial QP
    QP = m_disCtx.PreviousQP;
    prevQP = QP;
    foundBestQP = 0;

    // set working bit sizes
    extraBitSize = m_extraBitSize;
    maxBitSize = m_maxMbBitSize;

    // main working cycle
    do
    {
        // set the destination pointer
        psCoeffs = m_pWriteCoeffs;
        // update CBP
        lumaCBP = 0;
        chromaCBP = 0;
        CompensateMotionLumaBMacroBlock(pMCBlock, 16);
        // obtain luma residual for blocks Y0 Y1 Y2 Y4
        ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[0], pitch,
                             pMCBlock, 16, psTemp, 16);
        ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[0] + 8, pitch,
                             pMCBlock + 8, 16, psTemp + 64*1, 16);

        ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[0] + 8*pitch, pitch,
                             pMCBlock + 8 * 16, 16, psTemp + 64*2, 16);
        ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[0] + 8*pitch + 8, pitch,
                             pMCBlock + 8 * 16 + 8, 16, psTemp + 64*3, 16);
        // obtain chroma residual
        // chroma blocks U0 V0
        chromaBlockCount = 2;
        CompensateMotionChromaBMacroBlock(pMCBlock, pMCBlock + 8, 16);
        ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[1], pitch,
                             pMCBlock, 16, psTemp + 64 * 4, 16);
        ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[2], pitch,
                             pMCBlock + 8, 16, psTemp + 64 * 5, 16);
        if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
        {
            // chroma blocks U1 V1
            chromaBlockCount = 4;
            ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[1] + pitch * 8, pitch,
                            pMCBlock + 8 * 16, 16, psTemp + 64 * 6, 16);
            ippiSub8x8_8u16s_C1R(m_disCtx.m_pSrcPlanes8u[2] + pitch * 8, pitch,
                            pMCBlock + 8 * 16 + 8, 16, psTemp + 64 * 7, 16);
        }

        // transform and quantize luma blocks
        Ipp32u quantValue = AVSQuantTable[QP & 0x3f];
        for (i = 0; i < 4; i++)
        {
            ippiTransformQuant8x8Fwd_AVS_16s_C1(psTemp + i * 64, psTemp + i * 64,
                                                &blockHasCoeffs, QP, 0);
            if (blockHasCoeffs)
            {
                lumaCBP |= (1 << i);
                countCoeff = 0;
                memcpy(psCoeffs, psTemp + i * 64, 64*sizeof(Ipp16s));
                psCoeffs += 64;
            }
        }
        // transform and Quantize Chroma blocks
        chromaQP = AVSChromaQPTable[QP];
        quantValue = AVSQuantTable[chromaQP & 0x3f];
        for (i = 0; i < chromaBlockCount; i++)
        {
            Ipp32s ii = i + 4;
            ippiTransformQuant8x8Fwd_AVS_16s_C1(psTemp + ii * 64, psTemp + ii * 64,
                                                &blockHasCoeffs, chromaQP, 0);
            if (blockHasCoeffs)
            {
                chromaCBP |= (1 << i);
                countCoeff = 0;
                memcpy(psCoeffs, psTemp + ii * 64, 64*sizeof(Ipp16s));
                psCoeffs += 64;
            }
        }

        // set new macroblock parameter
        // it is required to calculate required MB size correctly
        m_pMbInfo->QP = QP;
        m_pMbInfo->MbCBP = (chromaCBP << 4) | lumaCBP;

        // check break cycle condition
        if (m_disCtx.FixedQP)
            break;

        // calculate required bit stream for this macroblock
        mbBitSize = GetSizePMacroBlock();

        //
        // check quit conditions
        //
        // we reached a QP bound, go exit
        if (((mbBitSize < maxBitSize) && (0 == QP)) ||
            ((mbBitSize > maxBitSize) && (63 == QP)))
        {
            break;
        }
        // boundary of mb_qp_delta is -32..31
        mb_qp_delta = m_pMbInfo->QP - m_encCtx.PreviousQP;
        if ((31 < (mb_qp_delta + 1) ) || ((-32 > mb_qp_delta - 1) ))
        {
            break;
        }
        else
        {
            // this macroblock is pretty cool fitting the required size
            if ((mbBitSize >= maxBitSize) &&
                (mbBitSize <= maxBitSize + extraBitSize))
            {
                break;
            }
            // the macroblock doesn't fit the size, continue coding
            if (mbBitSize < maxBitSize)
            {
                // it is a QP, that we are looking for
                if (prevQP < QP)
                {
                    break;
                }

                // set new QP
                prevQP = QP;
                QP -= 1;
            }
            else
            {
                // set new QP
                prevQP = QP;
                QP += 1;
            }
        }

        // it is unreal condition, just to make the loop infinite
    } while (QP != prevQP);

    // save previous QP
    m_disCtx.PreviousQP = QP;

    // set the coefficient pointer
    m_pWriteCoeffs = psCoeffs;

    // update available extra bits
    if (0 == m_disCtx.FixedQP)
    {
        m_extraBitSize = IPP_MAX(0, (Ipp32s) (maxBitSize + extraBitSize - mbBitSize));
    }

    // some additional conditions
    if (B_Skip == m_pMbInfo->MbType)
    {
        m_pMbInfo->MbCBP = 0;
    }

    WRITE_TRACE_LOG(_dis_tst, "CurrentQP", m_pMbInfo->QP);

    WRITE_TRACE_LOG_BYTE_BLOCK(_dis_tst, "Y block", m_disCtx.m_pPlanes8u[0], m_disCtx.m_iPitch, 16, 16);

    WRITE_TRACE_LOG(_dis_tst, "chromaQP", chromaQP);

    WRITE_TRACE_LOG_BYTE_BLOCK(_dis_tst, "U block", m_disCtx.m_pPlanes8u[1], m_disCtx.m_iPitch, 8, 8);
    WRITE_TRACE_LOG_BYTE_BLOCK(_dis_tst, "V block", m_disCtx.m_pPlanes8u[2], m_disCtx.m_iPitch, 8, 8);

} // void AVSCompressor::DisassembleBMacroBlock(void)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_ENCODER)
